package io.gitee.sections.sequence.core.impl;

import io.gitee.sections.sequence.core.SequenceDefinition;
import io.gitee.sections.sequence.core.SequenceGenerator;
import io.gitee.sections.sequence.core.cache.CacheProvider;
import io.gitee.sections.sequence.core.dao.DataAccessor;

import java.util.*;

public class SequenceManager implements SequenceGenerator {
    public static final String DEFAULT_SEQUENCE_NAME = "default";
    private Map<String, Sequence> sequences = new HashMap<>();
    private DataAccessor dataAccessor;
    private List<CacheProvider> cacheProviders;

    public SequenceManager(DataAccessor dataAccessor, List<CacheProvider> cacheProviders) {
        this.dataAccessor = dataAccessor;
        this.cacheProviders = cacheProviders;
        this.load(DEFAULT_SEQUENCE_NAME);
    }

    @Override
    public Long next() {
        return next(DEFAULT_SEQUENCE_NAME);
    }

    @Override
    public Long next(String key) {
        if (sequences.containsKey(key)) {
            return sequences.get(key).next();
        } else {
            this.load(key);
            return this.next(key);
        }
    }

    /**
     * 1 优先从数据库加载已存在的序列配置信息
     * 2 其次通过默认序列复制一份配置信息，默认配置信息可以通过yml修改
     * 3 最后直接新建一份配置信息，基于代码里面写死的初始值
     */
    public void load(String key) {
        SequenceDefinition definition;
        final Optional<SequenceDefinition> definitionFromDB = dataAccessor.find(key);
        if (definitionFromDB.isPresent()) {
            definition = definitionFromDB.get();
        } else {
            final Sequence defaultSequence = this.sequences.get(DEFAULT_SEQUENCE_NAME);
            if (Objects.nonNull(defaultSequence)) {
                final SequenceDefinition template = defaultSequence.getDefinition();
                template.setKey(key);
                definition = template;
            } else {
                definition = new SequenceDefinition(key);
            }
        }
        load(Collections.singletonList(definition));
    }

    public void load(List<SequenceDefinition> sequenceDefinitions) {
        sequenceDefinitions.forEach(definition -> {
            final Sequence sequence = new Sequence(definition, cacheProviders);
            sequence.initialize(dataAccessor);
            this.sequences.put(definition.getKey(), sequence);
        });
    }
}
